Serializers 序列化组件


只要继承了 APIView 就可以使用序列化组件

1. 序列化组件的基本使用

  • 序列化组件 的使用和 form 组件的使用类似,都需要定义一个类,而这个类就叫: 序列化类

# serializer.py

from rest_framework import serializers


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField(label='用户名')
    email = serializers.CharField(label='邮箱')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class PublishView(APIView):
    def get(self, request):
        publish_obj = Publish.objects.all()

        ps = PublishSerializers(publish_obj, many=True)  # 通过你所创建的 序列化类 序列化数据
        data = ps.data  # Serializer对象.data 获取序列化后的数据 -> [OrderedDict([('name', '东莞出版社'), ('email', '123@qq.com')]), OrderedDict([('name', '广州出版社'), ('email', '123@qq.com')])]

        return Response(data)

# 接口: http://127.0.0.1:8000/publish/

# 请求类型: GET

# 结果:

[
    {
        "name": "东莞出版社",
        "email": "123@qq.com"
    },
    {
        "name": "广州出版社",
        "email": "123@qq.com"
    }
]

2. many 参数

  • many 参数: 说明所要序列化的数据是 queryset对象(查询到的数据列表)还是 查询到的数据对象

  • many=True -> 所要序列化的数据是 queryset对象(查询到的数据列表)

# serializer.py

from rest_framework import serializers


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField(label='用户名')
    email = serializers.CharField(label='邮箱')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class PublishView(APIView):
    def get(self, request):
        publish_obj = Publish.objects.all()

        ps = PublishSerializers(publish_obj, many=True)  # 所要序列化的数据是 queryset对象(查询到的数据列表)
        data = ps.data

        return Response(data)

# 接口: http://127.0.0.1:8000/publish/

# 请求类型: GET

# 结果:

[
    {
        "name": "东莞出版社",
        "email": "123@qq.com"
    },
    {
        "name": "广州出版社",
        "email": "123@qq.com"
    }
]

  • many=False -> 所要序列化的数据是查询到的数据对象,且 many 参数默认值为False,所以在序列化查询到的数据对象的时候 many 参数可以不用传

# serializer.py

from rest_framework import serializers


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField(label='用户名')
    email = serializers.CharField(label='邮箱')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class PublishView(APIView):
    def get(self, request):
        publish_obj = Publish.objects.all().first()

# ps = PublishSerializers(publish_obj, many=False)  # 所要序列化的数据是查询到的数据对象
        ps = PublishSerializers(publish_obj)  # 所要序列化的数据是查询到的数据对象
        data = ps.data

        return Response(data)

# 接口: http://127.0.0.1:8000/publish/

# 请求类型: GET

# 结果:

{
    "name": "东莞出版社",
    "email": "123@qq.com"
}

3. Response() 方法

  • rest-framework 所提供的 Response() 方法实际上也是继承了 HttpResponse ,只是在 HttpResponse 的基础上扩展了一些功能
  • 如果使用了 rest-framework 那么就使用它所提供的 Response() 方法将数据发送给前端

# serializer.py

from rest_framework import serializers


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField(label='用户名')
    email = serializers.CharField(label='邮箱')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class PublishView(APIView):
    def get(self, request):
        publish_obj = Publish.objects.all().first()
        ps = PublishSerializers(publish_obj)
        data = ps.data

        return Response(data)

  • Response() 方法可以直接对 字典、列表、元祖、字符串、数字 进行发送

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response


class PublishView(APIView):

    def get(self, request):
        return Response({'a': 1, 'b': 2})
 # return Response([1, 2, 3])
        # return Response((1, 2, 3))
# return Response('字符串')
        # return Response(10)

4.序列化日期对象并且处理日期格式

  • format='日期格式'

# serializer.py

from rest_framework import serializers


class BookSerializers(serializers.Serializer):
    title = serializers.CharField(label='书籍名称')
    price = serializers.CharField(label='价格')
    pub_date = serializers.CharField(label='未处理的出版日期')
    production_date = serializers.DateTimeField(format='%Y-%m-%d %H:%M:%S')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True)
        data = bs.data
        return Response(data)

# 接口: http://127.0.0.1:8000/book/

# 请求类型: GET

# 结果:

[
    {
        "title": "三国演义",
        "price": "100",
        "pub_date": "2019-08-11",
        "production_date": "2019-08-11 18:07:19"
    },
    {
        "title": "红楼梦",
        "price": "200",
        "pub_date": "2019-08-11",
        "production_date": "2019-08-11 18:08:06"
    }
]

4.Serializer 类的 depth 参数说明

  • 作用: 序列化 一对多 或 多对多 字段

  • depth 的缺点: 获取很多与当前接口无用的字段数据(即: 获取到一对多或多对多字段中的全部数据)

  • depth 值的说明: 必须为数值
    • 当前值为0: 序列化出来的是 一对多 或 多对多字段 所保存的 id 值
    • 当前值为1: 序列化出来的是 一对多 或 多对多字段 所对应的表的数据
    • 当前值为2: 如果 一对多 或 多对多字段 中还有 一对多 或 多对多字段,那么就序列化出该 一对多 或 多对多字段 所对应的表的数据
    • 当前数值为n: ……

  • depth 的默认值: 0

  • depth 官方取值范围推荐: 1-10

  • depth = 0

# serializer.py

from rest_framework import serializers
from .models import *


class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'
depth = 1

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import *
from .models import *


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True)
        return Response(bs.data)

depth = 0

# 接口: http://127.0.0.1:8001/book/

# 请求类型: GET

# 结果:

[
    {
        "id": 2,
        "title": "三体(第二部)",
        "price": 100,
        "pub_date": "2012-12-12",
        "publish": 1,
        "authors": [
            1,
            2
        ]
    },
    {
        "id": 5,
        "title": "三国演义",
        "price": 100,
        "pub_date": "2012-12-12",
        "publish": 1,
        "authors": [
            1,
            2
        ]
    }
]

# --------------------------------------------------------------

# depth = 1

# 接口: http://127.0.0.1:8001/book/

# 请求类型: GET

# 结果:

[
    {
        "id": 2,
        "title": "三体(第二部)",
        "price": 100,
        "pub_date": "2012-12-12",
        "publish": {
            "id": 1,
            "name": "东莞出版社",
            "email": "123@qq.com"
        },
        "authors": [
            {
                "id": 1,
                "name": "Kevin",
                "age": 18
            },
            {
                "id": 2,
                "name": "Aimer",
                "age": 20
            }
        ]
    },
    {
        "id": 5,
        "title": "三国演义",
        "price": 100,
        "pub_date": "2012-12-12",
        "publish": {
            "id": 1,
            "name": "东莞出版社",
            "email": "123@qq.com"
        },
        "authors": [
            {
                "id": 1,
                "name": "Kevin",
                "age": 18
            },
            {
                "id": 2,
                "name": "Aimer",
                "age": 20
            }
        ]
    }
]

5.Serializer 类的 source 参数说明

  • source 参数一般用于序列化 choices 字段 和 一对多字段

  • source 参数可以序列化自身字段,但是没有意义
    • 即: username = serializers.CharField(source='username') 等同于 username = serializers.CharField()

  • source 参数的值支持像模板语言中的链式操作
    • source='get_choices字段_display' -> source='get_user_type_display'
    • source='一对多字段名.字段' -> source='publish.name'
    • source='多对多字段名.all' -> source='authors.all'

6. 序列化 choices 字段的方法

  • 使用 source 参数序列化 choices 字段

  • 如果不使用 source 参数序列化 choices 字段,那么在序列化 choices 字段的时候默认会取与该字段所保存的数值

  • 语法: source='get_choices字段_display'

# serializer.py

from rest_framework import serializers


class UserSerializers(serializers.Serializer):
    username = serializers.CharField(label='用户名')
    user_type = serializers.CharField(label='用户类型', source='get_user_type_display')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import *
from .models import *


class UserView(APIView):
    def get(self, request):
        user_list = User.objects.all()
        us = UserSerializers(user_list, many=True)
        print(us.data)  # [OrderedDict([('username', 'Kevin'), ('user_type', 'SVIP')]), OrderedDict([('username', 'Aimer'), ('user_type', 'VIP')]), OrderedDict([('username', 'Jack'), ('user_type', '普通用户')])]
        return Response(us.data)

# 接口: http://127.0.0.1:8001/user/

# 请求类型: GET

# 结果:

[
    {
        "username": "Kevin",
        "user_type": "SVIP"
    },
    {
        "username": "Aimer",
        "user_type": "VIP"
    },
    {
        "username": "Jack",
        "user_type": "普通用户"
    }
]

7.SerializerMethodField 自定义字段

  • SerializerMethodField() 字段方法的作用: 自定义字段的返回值

  • 使用场景: 当我们想返回一些自定义的值的时候,就可以使用 SerializerMethodField

  • 函数名定义规则: get_字段名

# serializer.py

from rest_framework import serializers


class BookSerializers(serializers.Serializer):
    title = serializers.CharField(label='书籍名称')
    price = serializers.CharField(label='价格')

    custom_field = serializers.SerializerMethodField()

    def get_custom_field(self, obj):
"""
        :param obj: obj 就是数据库中的一条数据
        :return: 所要显示的数据
        """
        price = obj.price
        return price + 100

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True)
        print(bs.data)  # [OrderedDict([('title', '三体'), ('price', '100'), ('custom_field', 200)]), OrderedDict([('title', '七大罪'), ('price', '50'), ('custom_field', 150)])]
        return Response(bs.data)

# 接口: http://127.0.0.1:8000/book/

# 请求类型: GET

# 结果:

[
    {
        "title": "三体",
        "price": "100",
        "custom_field": 200
    },
    {
        "title": "七大罪",
        "price": "50",
        "custom_field": 150
    }
]

8.context 参数(即: 在 serializers 中获取 request 对象

  • 视图类没有使用 serializer_class 参数

    • 通过 context 参数 serializers 可以接收视图传递过来的参数

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True, context={'request': request, 'username': 'Kevin', 'age': 18})
        return Response(bs.data)

# serializers.py

class BookSerializers(serializers.Serializer):
    title = serializers.CharField(label='书籍名称')
    price = serializers.CharField(label='价格')

    custom_field = serializers.SerializerMethodField()

    def get_custom_field(self, obj):
        print(self.context # {'request': <rest_framework.request.Request object at 0x0000025F75F0D668>, 'age': 18, 'username': 'Kevin'}
        request = self.context.get('request')
        username = self.context.get('username')
        age = self.context.get('age')
        return 1

  • 视图类使用了 serializer_class 参数

    • 当视图类使用了 serializer_class 参数,那么 serializers 默认只会接收到三个参数:

      • request -> request 对象,当前视图的请求对象
      • format -> 当前请求期望返回的数据格式
      • view -> 当前请求的类视图对象,相当于视图类中的 self

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *
from rest_framework import viewsets


class BookView(viewsets.ModelViewSet):
    queryset = Book.objects.all()
serializer_class = BookSerializers

# serializers.py

class BookSerializers(serializers.Serializer):
    title = serializers.CharField(label='书籍名称')
    price = serializers.CharField(label='价格')

    custom_field = serializers.SerializerMethodField()

    def get_custom_field(self, obj):
        print(self.context)  # {'view': <api.views.BookView object at 0x00000229E4FCA5C0>, 'request': <rest_framework.request.Request object at 0x00000229E5077898>, 'format': None}
        request = self.context.get('request')
        response_format = self.context.get('format')
        view = self.context.get('view')
        return 1

9. 序列化组件传递或不传递 request 对象的区别

  • 不传递 request 对象

# models.py

class Picture(models.Model):
    title = models.CharField(max_length=32, verbose_name='标题')
    img = models.ImageField(upload_to='img/', verbose_name='图片')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer.picture import PictureSerializers
from app01.models import *


class PictureView(APIView):
    def get(self, request, *args, **kwargs):
        picture_queryset = Picture.objects.all()
        ps = PictureSerializers(picture_queryset, many=True)
        data = ps.data
        return Response(data)

# serializers.py

from rest_framework import serializers


class PictureSerializers(serializers.Serializer):
    title = serializers.CharField(label='标题')
    img = serializers.FileField(label='图片')

# 接口: http://127.0.0.1:8000/api/picture/

# 请求类型: GET

# 结果:

[
    {
        "title": "三国演义",
"img": "/media/img/20200526210016.png"
    },
    {
        "title": "西游记",
"img": "/media/img/GVOMVF_OVT0RISCUC2G_McjmBMt.png"
    },
    {
        "title": "红楼梦",
"img": "/media/img/GVOMVF_OVT0RISCUC2G_ospZsQ2.png"
    }
]

  • 传递 request 对象

    • 注意: 在 django_rest_framework 官网提及到如果涉及到路径字段,应当传递 request 对象,以便生成完整的 url 而不是相对 url

# models.py

class Picture(models.Model):
    title = models.CharField(max_length=32, verbose_name='标题')
    img = models.ImageField(upload_to='img/', verbose_name='图片')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer.picture import PictureSerializers
from app01.models import *


class PictureView(APIView):
    def get(self, request, *args, **kwargs):
        picture_queryset = Picture.objects.all()
        ps = PictureSerializers(picture_queryset, many=True, context={'request': request})
        data = ps.data
        return Response(data)

# serializers.py

from rest_framework import serializers


class PictureSerializers(serializers.Serializer):
    title = serializers.CharField(label='标题')
    img = serializers.FileField(label='图片')

# 接口: http://127.0.0.1:8000/api/picture/

# 请求类型: GET

# 结果:

[
    {
        "id": 9,
        "title": "三国演义",
"img": "http://127.0.0.1:8000/media/img/20200526210016.png"
    },
    {
        "id": 10,
        "title": "西游记",
"img": "http://127.0.0.1:8000/media/img/GVOMVF_OVT0RISCUC2G_McjmBMt.png"
    },
    {
        "id": 11,
        "title": "红楼梦",
"img": "http://127.0.0.1:8000/media/img/GVOMVF_OVT0RISCUC2G_ospZsQ2.png"
    }
]

10. 序列化一对多字段的方法 -> 正向查询

  • 使用 source 参数序列化一对多字段

  • 如果不使用 source 参数序列化一对多字段,那么在序列化一对多字段的时候默认会取与该字段所关联的表类里面的 __str__ 的返回值

  • 语法: source='一对多字段名.字段'

# serializer.py

from rest_framework import serializers


class BookSerializers(serializers.Serializer):
    title = serializers.CharField(label='书籍名称')
    price = serializers.CharField(label='价格')
    pub_date = serializers.CharField(label='出版日期')
    publish = serializers.CharField(label='出版社', source='publish.name')  # 序列化一对多字段,且在序列化一对多字段的时候默认取 publish 表下的 name 字段的数据

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True)
        print(bs.data)  # [OrderedDict([('title', '西游记'), ('price', '111'), ('pub_date', None), ('publish', '东莞出版社')]), OrderedDict([('title', '三体'), ('price', '222'), ('pub_date', None), ('publish', '东莞出版社')])]
        return Response(bs.data)

# 接口: http://127.0.0.1:8000/book/

# 请求类型: GET

# 结果:

[
    {
        "title": "三体",
        "price": "222",
        "pub_date": null,
        "publish": "东莞出版社"
    },
    {
        "title": "三国演义",
        "price": "100",
        "pub_date": "2012-12-12",
        "publish": "东莞出版社"
    }
]

11. 序列化一对多字段的方法 -> 反向查询

  • 使用 SerializerMethodField() 字段方法,自定义函数返回值,实现序列化一对多字段的反向查询

  • SerializerMethodField() 字段方法的作用: 自定义字段的返回值

  • 函数名定义规则: get_字段名

# serializer.py

from rest_framework import serializers


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField(label='出版社名称')
    email = serializers.CharField(label='邮箱')

    book = serializers.SerializerMethodField()

    def get_book(self, obj):
"""
        :param obj: 当前数据的对象(即: 和查询到的数据对象是一样的)
        """
        temp = []
        for book in obj.book_set.all():  # 反向查询
            temp.append({
                'title': book.title,
                'price': book.price,
                'pub_date': book.pub_date,
            })
        return temp

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import *
from .models import *


class PublishView(APIView):
    def get(self, request):
        publish_list = Publish.objects.all()
        ps = PublishSerializers(publish_list, many=True)
        return Response(ps.data)

# 接口: http://127.0.0.1:8001/publish/

# 请求类型: GET

# 结果:

[
    {
        "name": "东莞出版社",
        "email": "123@qq.com",
        "book": [
            {
                "pub_date": "2012-12-12",
                "price": 100,
                "title": "三体(第二部)"
            },
            {
                "pub_date": "2012-12-12",
                "price": 100,
                "title": "三国演义"
            },
            {
                "pub_date": "2012-12-12",
                "price": 100,
                "title": "红楼梦"
            }
        ]
    },
    {
        "name": "广州出版社",
        "email": "123@qq.com",
        "book": []
    }
]

12. 序列化多对多字段的方法 -> 正向查询

  • 使用 SerializerMethodField() 字段方法,自定义函数返回值,实现序列化多对多字段的正向查询

  • SerializerMethodField() 字段方法的作用: 自定义字段的返回值

  • 函数名定义规则: get_字段名

# serializer.py

from rest_framework import serializers


class BookSerializers(serializers.Serializer):
    title = serializers.CharField(label='书籍名称')
    price = serializers.CharField(label='价格')
    pub_date = serializers.CharField(label='出版日期')
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
"""
        :param obj: 当前数据的对象(即: 和查询到的数据对象是一样的)
        """
        temp = []
        for author in obj.authors.all():
            temp.append(author.name)
        return temp

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True)
        print(bs.data)  # [OrderedDict([('title', '西游记'), ('price', '111'), ('pub_date', None), ('authors', ['Kevin'])]), OrderedDict([('title', '三体'), ('price', '222'), ('pub_date', None), ('authors', ['Aimer'])])]
        return Response(bs.data)

# 接口: http://127.0.0.1:8000/book/

# 请求类型: GET

# 结果:

[
    {
        "title": "三体",
        "price": "222",
        "pub_date": null,
        "authors": [
            "Aimer"
        ]
    },
    {
        "title": "三国演义",
        "price": "100",
        "pub_date": "2012-12-12",
        "authors": [
            "Kevin",
            "Aimer"
        ]
    }
]

13. 序列化多对多字段的方法 -> 反向查询

  • 使用 SerializerMethodField() 字段方法,自定义函数返回值,实现序列化多对多字段的反向查询

  • SerializerMethodField() 字段方法的作用: 自定义字段的返回值

  • 函数名定义规则: get_字段名

# serializer.py

from rest_framework import serializers


class AuthorSerializers(serializers.Serializer):
    name = serializers.CharField(label='作者名称')
    age = serializers.IntegerField(label='年龄')

    book = serializers.SerializerMethodField()

    def get_book(self, obj):
"""
        :param obj: 当前数据的对象(即: 和查询到的数据对象是一样的)
        """
        temp = []
        for book in obj.book_set.all():  # 反向查询
            temp.append({
                'title': book.title,
                'price': book.price,
                'pub_date': book.pub_date,
            })
        return temp

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import *
from .models import *


class AuthorView(APIView):
    def get(self, request):
        author_list = Author.objects.all()
        aus = AuthorSerializers(author_list, many=True)
        return Response(aus.data)

# 接口: http://127.0.0.1:8001/author/

# 请求类型: GET

# 结果:

[
    {
        "name": "Kevin",
        "age": 18,
        "book": [
            {
                "title": "红楼梦",
                "price": 100,
                "pub_date": "2012-12-12"
            },
            {
                "title": "勿忘我",
                "price": 100,
                "pub_date": "2012-12-12"
            }
        ]
    },
    {
        "name": "Aimer",
        "age": 20,
        "book": [
            {
                "title": "三体(第二部)",
                "price": 100,
                "pub_date": "2012-12-12"
            },
            {
                "title": "三国演义",
                "price": 100,
                "pub_date": "2012-12-12"
            },
            {
                "title": "超人",
                "price": 100,
                "pub_date": "2012-12-12"
            }
        ]
    }
]

14. 验证数据是否有误

  • 一般都是验证提交过来的数据

  • 这里的验证数据功能和Form组件或者ModelForm是一样的

# serializer.py

from rest_framework import serializers


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField(error_messages={'required': '出版社名称不能为空'})
    email = serializers.CharField(error_messages={'required': '邮箱不能为空'})

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class PublishView(APIView):
    def post(self, request):
        ps = PublishSerializers(data=request.data)  # 将提交过来的数据传入data参数中进行验证
        if ps.is_valid():  # 验证提交过来的数据
            print(ps.validated_data)  # 获取验证通过的数据 -> OrderedDict([('name', '深圳出版社'), ('email', '123@qq.com')])
            publish = Publish.objects.create(**ps.validated_data)  # 将数据保存到数据库
            p_ps = PublishSerializers(publish)  # 序列化数据
            return Response(p_ps.data)
        else:
            print(ps.errors)  # 获取验证失败后的错误信息
            return Response(ps.errors)

# 接口说明: 添加出版社

# 接口: http://127.0.0.1:8000/publish/

# 请求类型: POST

# 发送数据的编码格式: application/json -> 即: json 格式

# 所要发送的数据: {"name":"上海出版社","email":"123@qq.com"}

# 结果:

{
    "name": "上海出版社",
    "email": "123@qq.com"
}

15. 验证器

  • 验证器相当于局部钩子函数,定义在 serializers 类字段中

# serializer.py

import re
from rest_framework import serializers


def phone_validator(value):
 """
    :param value: 当前字段的内容
    :return: 验证通过无需返回任何内容,验证失败直接抛出异常
    """
    if not re.match(r'^(1[3|4|5|6|7|8|9]\d{9})$', value):
        raise serializers.ValidationError('手机格式错误')


class UserInfoSerializers(serializers.Serializer):
    username = serializers.CharField(label='用户名')
    phone = serializers.CharField(label='电话号码', validators=[phone_validator])

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer.picture import UserInfoSerializers
from app01.models import *


class UserInfoView(APIView):
    def post(self, request, *args, **kwargs):
        us = UserInfoSerializers(data=request.data)
        data = {}
        if us.is_valid():
            print(us.validated_data)  # 验证通过的数据 -> 有序字典: OrderedDict([('username', 'Kevin'), ('phone', '13022024070')])
            userinfo = UserInfo.objects.create(**us.validated_data)  # 将数据保存到数据库
            u_us = UserInfoSerializers(userinfo)
            data = u_us.data
        else:
            data = us.errors
        return Response(data)

16. 部分更新

  • 默认情况下,序列化器必须传递所有必填字段的值,否则就会引发验证错误。你可以使用 partial 参数来允许部分更新

# models.py

class UserInfo(models.Model):
    username = models.CharField(max_length=32, verbose_name='用户名')
    phone = models.CharField(max_length=32, verbose_name='电话号码')
    age = models.IntegerField(verbose_name='年龄')

# serializer.py

from rest_framework import serializers


class UserInfoSerializers(serializers.Serializer):
    username = serializers.CharField(label='用户名')
    phone = serializers.CharField(label='电话号码')
    age = serializers.IntegerField(label='年龄')

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer.picture import PictureSerializers, UserInfoSerializers
from app01.models import *


class UserInfoView(APIView):
    def put(self, request, *args, **kwargs):
        data = {}
        pk = kwargs.get('pk')
        user_queryset = UserInfo.objects.filter(pk=pk)
        user_obj = user_queryset.first()

        us = UserInfoSerializers(data=request.data, partial=True)

        if us.is_valid():
            user_obj.phone = request.data.get('phone')
            user_obj.age = int(request.data.get('age'))
            user_obj.save()
            u_us = UserInfoSerializers(user_obj)
            data = u_us.data
        else:
            data = us.errors
        return Response(data)

# 接口: http://127.0.0.1:8000/api/userinfo/13/

# 请求类型: PUT

# 传递的参数: 

phone = 13038802402

age = 22

# 结果:

{
    "username": "Kevin",
    "phone": "13038802402",
    "age": 22
}

序列化数据的方式


这里的序列化数据指的是将 queryset对象 或 查询到的数据对象 转换成 列表 或 字典,然后将序列化好的数据发送给前端

1. 序列化 queryset 对象(查询到的数据列表)

  • 方式一

# views.py

from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from rest_framework.views import APIView
from .models import *


class PublishView(APIView):
    def get(self, request):
        publish_queryset = Publish.objects.all().values('name', 'email')
        publish_list = list(publish_queryset)
        print(publish_list)  # [{'name': '东莞出版社', 'email': '123@qq.com'}, {'name': '广州出版社', 'email': '123@qq.com'}]
        return JsonResponse(publish_list, safe=False)

  • 方式二

# views.py

from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from rest_framework.views import APIView
from django.forms.models import model_to_dict
from .models import *


class PublishView(APIView):
    def get(self, request):
        publish_queryset = Publish.objects.all()
        publish_list = []
        for publish in publish_queryset:
            publish_list.append(model_to_dict(publish))
        print(publish_list)  # [{'id': 1, 'name': '东莞出版社', 'email': '123@qq.com'}, {'id': 2, 'name': '广州出版社', 'email': '123@qq.com'}]
        return JsonResponse(publish_list, safe=False)

  • 方式三

# views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from django.core import serializers
from .models import *


class PublishView(APIView):
    def get(self, request):
        publish_queryset = Publish.objects.all()
        publish_list = serializers.serialize("json", publish_queryset)
        print(publish_list)  # [{"model": "app01.publish", "pk": 1, "fields": {"name": "\u4e1c\u839e\u51fa\u7248\u793e", "email": "123@qq.com"'+'}'+'}'}}, {"model": "app01.publish", "pk": 2, "fields": {"name": "\u5e7f\u5dde\u51fa\u7248\u793e", "email": "123@qq.com"'+'}'+'}'}}]
        return HttpResponse(publish_list)

  • 方式四 -> 推荐使用

# serializer.py

from rest_framework import serializers


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField(label='出版社名称')
    email = serializers.CharField(label='邮箱')

# views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class PublishView(APIView):
    def get(self, request):
        publish_queryset = Publish.objects.all().values('name', 'email')
        ps = PublishSerializers(publish_queryset, many=True)
        print(ps.data)  # 获取序列化之后的数据 -> [OrderedDict([('name', '东莞出版社'), ('email', '123@qq.com')]), OrderedDict([('name', '广州出版社'), ('email', '123@qq.com')])]
        return Response(ps.data)

2. 序列化查询到的数据对象

  • 方式一

# views.py

from django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from rest_framework.views import APIView
from django.forms.models import model_to_dict
from .models import *


class PublishView(APIView):
    def get(self, request):
        publish_obj = Publish.objects.all().first()
        publish_dict = model_to_dict(publish_obj)
        print(publish_dict)  # {'email': '123@qq.com', 'name': '东莞出版社', 'id': 1}
        return JsonResponse(publish_dict)

  • 方式二

# serializer.py

from rest_framework import serializers


class PublishSerializers(serializers.Serializer):
    name = serializers.CharField(label='出版社名称')
    email = serializers.CharField(label='邮箱')

# views.py

from django.shortcuts import render, HttpResponse
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import *
from .serializer import *


class PublishView(APIView):
    def get(self, request):
        publish_obj = Publish.objects.all().first()
        ps = PublishSerializers(publish_obj)
        print(ps.data)  # 获取序列化之后的数据 -> {'email': '123@qq.com', 'name': '东莞出版社'}
        return Response(ps.data)

钩子函数


  • 什么是钩子函数: Django源码通过反射找到指定前缀的函数并且执行该函数,即: 该指定前缀的函数就是钩子函数

  • 什么时候使用钩子函数: 当 Serializers 组件所提供的校验规则无法满足你的校验,那么可以使用钩子函数自定义校验规则

1.局部钩子函数

  • 使用场景: 对某一个字段进行校验,如: 在注册的时候判断用户名字段是否已经被注册过了

  • 在 Serializers 类中定义 validate_字段名() 方法,就能够实现对特定字段进行校验

serializer/userinfo.py

from rest_framework import serializers
from app01.models import *


class UserInfoSerializers(serializers.Serializer):
    username = serializers.CharField(label='用户名')

# 定义局部钩子(validate_字段名() 方法),用来校验username字段
    def validate_username(self, value):
"""
        :param value: 当前字段的数据
        :return: 通过验证的数据
        """
        username_obj = UserInfo.objects.filter(username=value).first()
        if username_obj:
            raise serializers.ValidationError('用户名已注册')
        return value

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer.userinfo import UserInfoSerializers
from app01.models import *


class UserInfoView(APIView):
    def post(self, request):
        ser = UserInfoSerializers(data=request.data)  # 将提交过来的数据传入data参数中进行验证
        if ser.is_valid():  # 验证提交过来的数据
            print(ser.validated_data)  # 获取验证通过的数据 -> 有序字典 OrderedDict([('username', 'Kevin')])
            userinfo_obj = UserInfo.objects.create(**ser.validated_data)  # 将数据保存到数据库
            ser_data = UserInfoSerializers(userinfo_obj)  # 序列化数据
            return Response(ser_data.data)
        else:
            print(ser.errors)  # 获取验证失败后的错误信息 -> {'username': [ErrorDetail(string='用户名已注册', code='invalid')]}
            return Response(ser.errors)

2.全局钩子函数

  • 使用场景: 获取所有已经通过验证的字段,然后取某几个字段进行二次判断,如: 校验两次密码是否一致

  • 全局钩子的说明: 当全部校验通过后才会执行clean()方法,且在clean()方法中一定可以获取到所有已经通过验证的数据

  • 在Form类中定义 clean() 方法,就能够实现对字段进行全局校验

# serializer/userinfo.py

from rest_framework import serializers


class UserInfoSerializers(serializers.Serializer):
    username = serializers.CharField(label='用户名')
    password = serializers.CharField(label='密码')
    confirm_password = serializers.CharField()

# 全局钩子
    def validate(self, attrs):
"""
        :param attrs: 所有验证通过的数据
        :return: 验证通过的数据
        """
        print(attrs)  # 有序字典 -> OrderedDict([('username', 'Kevin'), ('password', '123'), ('confirm_password', '1234')])
        if attrs.get('password') != attrs.get('confirm_password'):
            raise serializers.ValidationError("两次密码不一致")
        attrs.pop('confirm_password')
        return attrs

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer.userinfo import UserInfoSerializers
from app01.models import *


class UserInfoView(APIView):
    def post(self, request):
        ser = UserInfoSerializers(data=request.data)  # 将提交过来的数据传入data参数中进行验证
        if ser.is_valid():  # 验证提交过来的数据
            print(ser.validated_data)  # 获取验证通过的数据 -> 有序字典 OrderedDict([('username', 'Kevin'), ('password', '123')])
            UserInfo.objects.create(**ser.validated_data)  # 将数据保存到数据库
            return Response({'msg': '添加成功'})
        else:
            print(ser.errors)  # 获取验证失败后的错误信息 -> {'non_field_errors': [ErrorDetail(string='两次密码不一致', code='invalid')]}
            return Response(ser.errors)